Toponym Search by Figure-Eight-Knots


by Ashok Khosla, Di Hi, and Kylie Quave - November 2024

2. Search Criteria:

Based on the examples in the Puruchuco hierarchy, let’s first look for all khipus containing

  • First cord group/cluster is seriated
  • First cord contains a figure-eight-knot
Code
import os
import numpy as np
import pandas as pd
from pandas import Series, DataFrame
from collections import Counter

import qollqa_chuspa as qc  # A Khipu Maker is known (in Quechua) as a Khipu Kamayuq
import utils_loom as uloom
import utils_khipu as ukhipu
import utils_pandas as upanda
    
# Plotly
import plotly
from plotly.offline import iplot, init_notebook_mode
import plotly.graph_objs as go
import plotly.express as px
import plotly.figure_factory as ff
plotly.offline.init_notebook_mode(connected = False)

# Load all khipus
(khipu_dict, all_khipus) = qc.fetch_khipus()
khipu_names = ukhipu.kfg_order(khipu_dict.keys())
Code
def is_potential_toponym(aKhipu):
    """Returns True if the khipu has a toponym, False otherwise."""
    first_cord_group = aKhipu[0]
    first_cord = first_cord_group[0]
    is_possible_toponym = first_cord.has_eight_knot(include_subsidiaries=False) and first_cord_group.is_seriated_group() and (aKhipu.num_top_cords()== 0)
    return is_possible_toponym

potential_toponym_khipus = [aKhipu for aKhipu in all_khipus if is_potential_toponym(aKhipu)]
potential_toponym_khipu_names = uloom.multiline(", ".join([akhipu.kfg_name() for akhipu in potential_toponym_khipus]), split_char = ", ", continuation_char=",\n\t")
print(f"There are {len(potential_toponym_khipus)} potential toponym khipus.")
print(f"\t{potential_toponym_khipu_names}")
There are 62 potential toponym khipus.
    CM009, KH0006, KH0010, KH0017, KH0037, KH0054, KH0066, KH0076, KH0077, KH0078,
    KH0090, KH0091, KH0092, KH0134, KH0141, KH0162, KH0166, KH0168, KH0175, KH0190,
    KH0226, KH0227, KH0231, KH0243, KH0285, KH0286, KH0297, KH0301, KH0303, KH0304,
    KH0305, KH0315, KH0328, KH0352, KH0360, KH0374, KH0406, KH0419, KH0434, KH0441,
    KH0480, KH0481, KH0496, KH0514, KH0521, KH0523, KH0527, KH0542, KH0551, KH0563,
    KH0565, KH0570, KH0595, KH0599, KH0657, KH0663, KH0664, KH0673, KH0689, KH0692,
    KH0702, RA001

We can still do some culling:

A quick glance at AS004, shows that the first 8 knot is part of a sum, followed by a top cord. Let’s eliminate all khipus with top cords, knowing that top cords are generally a hallmark of banded khipus. Since banded khipus are considered to be local, by Urton and Brezine’s definition, local they are unlikely to be toponymic (see Medrano and Khosla’s article How Can Data Science Contribute to Understanding the Khipu Code?).

Code
def is_potential_toponym(aKhipu):
    """Returns True if the khipu has a toponym, False otherwise."""
    first_cord_group = aKhipu[0]
    first_cord = first_cord_group[0]
    is_possible_toponym = first_cord.has_eight_knot(include_subsidiaries=False) and first_cord_group.is_seriated_group() and (aKhipu.num_top_cords()== 0)
    return is_possible_toponym

potential_toponym_khipus = [aKhipu for aKhipu in all_khipus if is_potential_toponym(aKhipu)]
potential_toponym_khipu_names = uloom.multiline(", ".join([akhipu.kfg_name() for akhipu in potential_toponym_khipus]), split_char = ", ", continuation_char=",\n\t")
print(f"There are {len(potential_toponym_khipus)} potential toponym khipus.")
print(f"\t{potential_toponym_khipu_names}")
There are 62 potential toponym khipus.
    CM009, KH0006, KH0010, KH0017, KH0037, KH0054, KH0066, KH0076, KH0077, KH0078,
    KH0090, KH0091, KH0092, KH0134, KH0141, KH0162, KH0166, KH0168, KH0175, KH0190,
    KH0226, KH0227, KH0231, KH0243, KH0285, KH0286, KH0297, KH0301, KH0303, KH0304,
    KH0305, KH0315, KH0328, KH0352, KH0360, KH0374, KH0406, KH0419, KH0434, KH0441,
    KH0480, KH0481, KH0496, KH0514, KH0521, KH0523, KH0527, KH0542, KH0551, KH0563,
    KH0565, KH0570, KH0595, KH0599, KH0657, KH0663, KH0664, KH0673, KH0689, KH0692,
    KH0702, RA001

Next let’s elimate any khipus where the first cord is a sum marker.

To this we build a concatenated dataframe of pendant_pendant_sums, indexed_pendant_sums, and colored_pendant_sums, and see if p1 occurs in any of the khipus sums

Code
# Ascher sums for a khipu
from fieldmark_ascher_pendant_pendant_sum import FieldmarkPendantPendantSum
from fieldmark_ascher_indexed_pendant_sum import FieldmarkIndexedPendantSum
from fieldmark_ascher_colored_pendant_sum import FieldmarkColoredPendantSum
pendant_pendant_sums_df = FieldmarkPendantPendantSum().relations_df()
indexed_pendant_sums_df = FieldmarkIndexedPendantSum().relations_df()
colored_pendant_sums_df = FieldmarkColoredPendantSum().relations_df()
potential_toponym_df = pd.concat([pendant_pendant_sums_df.copy(deep=True),
                                 indexed_pendant_sums_df.copy(deep=True),# .drop(columns=['summand_group_positions']),
                                 colored_pendant_sums_df.copy(deep=True)]) #.drop(columns=['Unnamed: 0'])
potential_toponym_df['sum_type'] = ['pendant_pendant_sum']*len(pendant_pendant_sums_df) + ['indexed_pendant_sum']*len(indexed_pendant_sums_df) + ['colored_pendant_sum']*len(colored_pendant_sums_df)
print(f"figure-eight-knots Sums Dataframe:")
# potential_toponym_df.drop(columns=['Unnamed: 0'], inplace=True)

# First cull is for khipus that have a potential toponym as noted above
potential_toponym_khipu_names = [akhipu.kfg_name() for akhipu in potential_toponym_khipus]
potential_toponym_df = potential_toponym_df[potential_toponym_df['kfg_name'].isin(potential_toponym_khipu_names)]
print(f"After first cull we have {potential_toponym_df['kfg_name'].nunique()} potential toponyms.")


potential_toponym_df.columns
# # Second cull is for khipus that don't have p1 in 'leading_exact_pendant_cord_name' or 'leading_close_pendant_cord_names' 
potential_toponym_df = potential_toponym_df[potential_toponym_df['left_exact_pendant_8knot_cord_name'].str.contains('p1') == False]
potential_toponym_df = potential_toponym_df[potential_toponym_df['left_close_pendant_cord_names'].str.contains('p1') == False]
print(f"After second cull we have {potential_toponym_df['kfg_name'].nunique()} potential toponyms.")

potential_toponym_kfg_names = potential_toponym_df['kfg_name'].unique().tolist()
potential_toponym_khipu_strrep = ", ".join(potential_toponym_kfg_names)
print(f"\nThere are {len(potential_toponym_kfg_names)} potential toponym khipus")
print(f"\t{potential_toponym_khipu_strrep}")
figure-eight-knots Sums Dataframe:
After first cull we have 39 potential toponyms.
After second cull we have 17 potential toponyms.

There are 17 potential toponym khipus
    CM009, KH0090, KH0141, KH0175, KH0297, KH0301, KH0305, KH0360, KH0406, KH0441, KH0595, KH0702, KH0162, KH0434, KH0480, KH0496, KH0521

Let’s build a table of these khipus with information about the first cord group:

  • KFG_Name
  • Number of cords (pendants and subsidiaries)
  • Cord names
  • Values for each cord
  • Knot sequence for each cord
  • Colors for each cord
Code
def build_toponym_record(aKhipu):
    kfg_name = aKhipu.kfg_name()
    first_group = aKhipu[0]
    group_cords = first_group.all_cords(include_subsidiaries=True)
    return {'KFG Name':kfg_name,
            '# Cords':len(group_cords),
            '# Pendants':len([aCord for aCord in group_cords if aCord.is_pendant_cord()]),
            '# Subsidiaries':len([aCord for aCord in group_cords if aCord.is_subsidiary_cord()]),
            'Cord Names':", ".join([aCord.pendant_name for aCord in group_cords]),
            'Cord Values':", ".join([str(aCord.knotted_value) for aCord in group_cords]),
            'Knot Sequence':" - ".join([aCord.knot_sequence() for aCord in group_cords]),
            'Cord Colors':", ".join([aCord.main_color() for aCord in group_cords]),
    }

the_topo_records = [build_toponym_record(khipu_dict[topo_khipu_name]) for topo_khipu_name in potential_toponym_kfg_names]
the_topo_df = pd.DataFrame(the_topo_records)
the_topo_df.head(20)
KFG Name # Cords # Pendants # Subsidiaries Cord Names Cord Values Knot Sequence Cord Colors
0 CM009 5 5 0 p1, p2, p3, p4, p5 1, 1, 1, 0, 0 E - S - S - - W, GG, W, AB, W:KB
1 KH0090 4 4 0 p1, p2, p3, p4 1, 3, 2, 6 E - E,L - E,E - L B, W, AB, W
2 KH0141 21 10 11 p1, p1s1, p2, p2s1, p3, p3s1, p4, p4s1, p5, p5... 21, 1, 20, 1, 21, 1, 12, 0, 20, 0, 12, 0, 20, ... S,E - E - S - E - S,E - E - S,L - - S - - S,... W-CB, CB, W-CB, W, B-CB, W, W-CB, B, B-CB, W, ...
3 KH0175 8 7 1 p1, p1s1, p2, p3, p4, p5, p6, p7 1, 20, 17, 16, 16, 18, 12, 16 E - S,L,L - S,L,E - S,L - S,L - S,L - S,L - S,L W, W, W:B, W:B, W:B, W:B, B, W:B
4 KH0297 5 5 0 p1, p2, p3, p4, p5 1, 1, 1, 1, 2 E - E - E - E - L MB, AB, MB, MB, GG:MB
5 KH0301 5 5 0 p1, p2, p3, p4, p5 1, 0, 0, 0, 0 E - - - - AB, W, MB, GG, W:CB
6 KH0305 5 5 0 p1, p2, p3, p4, p5 1, 0, 0, 0, 0 E - - - - AB, GG, W, AB, W:KB
7 KH0360 3 3 0 p1, p2, p3 1, 0, 5 E - - L W, AB, W
8 KH0406 7 4 3 p1, p2, p2s1, p3, p3s1, p4, p4s1 35, 20, 15, 11, 7, 11, 6 S,L,E - S,L,E - S,L - S,E - L - S,E - L AB, AB, MB:KB, AB, MB, AB, MB
9 KH0441 12 6 6 p1, p1s1, p1s2, p2, p2s1, p2s2, p3, p4, p5, p6... 18, 11, 17, 11, 10, 21, 28, 22, 32, 13, 41, 9 S,L,E - L,E,SP,U - L,E,L - E,S - L,E - E,S,S -... W, W, W, W, W, W, W, W, W, 0K, MB, W
10 KH0595 7 4 3 p1, p2, p2s1, p3, p3s1, p3s2, p4 1, 2, 2, 24, 0, 14, 1 E - L - L - S,L - - S,L - E YB:B, YB:B, YB:B, YB:B, KB, B, YB:B
11 KH0702 11 8 3 p1, p2, p3, p3s1, p4, p5, p5m1, p6, p6m1, p7, p8 1, 0, 0, 2, 2, 1, 0, 1, 0, 0, 1 E - - - L - L - E - - E - - - E W, W, AB, GL, PK:GL, W:AB, W:KB, W:AB, PG, AB, W
12 KH0162 81 72 9 p1, p2, p3, p4, p5, p6, p7, p7s1, p8, p9, p10,... 11, 10, 11, 11, 8, 9, 64, 11, 0, 0, 0, 0, 0, 6... S,E - S - S,E - S,E - L - L - S,L - S,E - - ... YB, YB, YB, YB, YB, YB, B, BB, B, B, B, B, B, ...
13 KH0434 11 9 2 p1, p1s1, p2, p3, p4, p5, p5s1, p6, p7, p8, p9 61, 4, 16, 0, 13, 43, 2, 10, 21, 13, 16 S,E - L - S,L - - S,L - S,L - L - L - S,E - S... W, MB, AB:MB, KB, AB, AB, MB, AB, AB, KB, AB
14 KH0480 8 8 0 p1, p2, p3, p4, p5, p6, p7, p8 1, 21, 4, 5, 20, 4, 2, 5 E - S,E - L - L - S - L - L - L W:AB, W:AB, W:AB, W:AB, W:MB, W:AB, W:AB, W:AB
15 KH0496 16 12 4 p1, p2, p3, p3s1, p4, p5, p6, p7, p7s1, p8, p9... 1, 2, 1, 0, 1, 1, 2, 3, 1, 1, 1, 1, 2, 1, 3, 1 E - L - E - - E - E - L - L - E - E - E - E -... AB, AB, AB, PK, AB, AB, W:AB, W:AB, AB-KB, W:A...
16 KH0521 18 18 0 p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, ... 7, 7, 12, 4, 2, 2, 10, 2, 3, 2, 2, 2, 2, 0, 5,... E,E,L,L,E - E,L,L,L - S,L - L - L - L - S - L ... W, AB, HB, AB, W-YY, W-MB, AB-GG, W-MB, BG-HB,...

Should we cull khipus with greater than 5 pendants? The color sequences, or knot values for groups with more than 5 seems to not fit the classic seriated pattern proposed by Brezine/Urton.

Code
the_topo_records = [build_toponym_record(khipu_dict[topo_khipu_name]) for topo_khipu_name in potential_toponym_kfg_names \
                        if khipu_dict[topo_khipu_name][0].num_pendant_cords() <= 5]
the_topo_df = pd.DataFrame(the_topo_records)
the_topo_df.head(20)
KFG Name # Cords # Pendants # Subsidiaries Cord Names Cord Values Knot Sequence Cord Colors
0 AS005 4 4 0 p1, p2, p3, p4 11, 1, 5, 0 E,E - E - L - MB, MB, MB, W:KB
1 AS077 4 4 0 p1, p2, p3, p4 1, 3, 2, 6 E - E,L - E,E - L B, W, AB, W
2 CM009 5 5 0 p1, p2, p3, p4, p5 1, 1, 1, 0, 0 E - S - S - - W, GG, W, AB, W:KB
3 QU007 7 4 3 p1, p2, p2s1, p3, p3s1, p3s2, p4 1, 2, 20, 24, 0, 14, 1 E - L - L - S,L - - S,L - E YB:B, YB:B, YB:B, YB:B, KB, B, YB:B
4 UR060 5 5 0 p1, p2, p3, p4, p5 1, 1, 1, 1, 2 E - E - E - E - L MB, AB, MB, MB, GG:MB
5 UR064 5 5 0 p1, p2, p3, p4, p5 1, 0, 0, 0, 0 E - - - - AB, W, MB, GG, W:CB
6 UR068 5 5 0 p1, p2, p3, p4, p5 1, 0, 0, 0, 0 E - - - - AB, GG, W, AB, W:KB
7 UR120 3 3 0 p1, p2, p3 1, 0, 5 E - - L W, AB, W
8 UR167 7 4 3 p1, p2, p2s1, p3, p3s1, p4, p4s1 35, 20, 15, 11, 7, 11, 6 S,L,E - S,L,E - S,L - S,E - L - S,E - L AB, AB, MB:KB, AB, MB, AB, MB

3. Conclusion

There is an old science joke that goes like this:

A mathematician, a physicist, and an engineer were traveling through Scotland when they saw a black sheep through the window of the train.
“Aha,” says the engineer, “I see that Scottish sheep are black.”
“Hmm,” says the physicist, “You mean that some Scottish sheep are black.”
“No,” says the mathematician, “All we know is that there is at least one sheep in Scotland, and that at least one side of that one sheep is black.”

In the case of the Puruchuco khipus, I suspect that one side of one sheep is black.